/* machine_uart.c */

#include <stdio.h>

#include "machine_uart.h"

#include "board_init.h"

/*
 * Declerations.
 */

/* Local functions. */
STATIC mp_obj_t machine_uart_obj_init_helper(const machine_uart_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);

/* Class method, which would be called by class name. */
STATIC     void machine_uart_obj_print(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind);
       mp_obj_t machine_uart_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args);


/* Instance methods, which would be called by class instance name. */
STATIC mp_obj_t machine_uart_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args);
STATIC mp_obj_t machine_uart_any(mp_obj_t self_in);

/* hal related functions. */
bool     machine_uart_tx_data(uint32_t uart_id, uint8_t tx_data);
uint8_t  machine_uart_rx_data(uint32_t uart_id);
uint32_t machine_uart_rx_any(uint32_t uart_id);
void     machine_uart_irq_handler(uint32_t uart_id);



const machine_uart_obj_t *uart_find(mp_obj_t user_obj);


/*
 * Functions.
 */



STATIC mp_obj_t machine_uart_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args)
{
    /* args[0] is machine_pin_obj_t. */
    //printf("machine_uart_init()");
    return machine_uart_obj_init_helper(args[0], n_args - 1, args + 1, kw_args);
}
MP_DEFINE_CONST_FUN_OBJ_KW(machine_uart_init_obj, 1, machine_uart_init);

STATIC mp_obj_t machine_uart_any(mp_obj_t self_in)
{
    machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
    return MP_OBJ_NEW_SMALL_INT(machine_uart_rx_any(self->uart_id));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_any_obj, machine_uart_any);


static const clock_ip_name_t uart_clock_arr[] = LPUART_CLOCKS;
void machine_uart_enable_clock(uint32_t uart_id)
{
    CLOCK_EnableClock(uart_clock_arr[uart_id]);
}

/* parameter list. */
typedef enum
{
    UART_INIT_ARG_BAUDRATE = 0,
    UART_INIT_ARG_BITS     = 1,
    UART_INIT_ARG_PARITY   = 2,
    UART_INIT_ARG_STOP     = 3,
} machine_uart_init_arg_t;

STATIC uint8_t machine_uart_rx_buff[MACHINE_UART_NUM][MACHINE_UART_XFER_BUFF_LEN];
STATIC rbuf_t  machine_uart_rx_rbuf[MACHINE_UART_NUM];

STATIC mp_obj_t machine_uart_obj_init_helper (
    const machine_uart_obj_t *self, /* machine_uart_obj_t类型的变量，包含硬件信息 */
    size_t n_args, /* 位置参数数量 */
    const mp_obj_t *pos_args, /* 位置参数清单 */
    mp_map_t *kw_args ) /* 关键字参数清单结构体 */
{
    //printf("%machine_adc_obj_init_helper().\r\n");
    static const mp_arg_t allowed_args[] =
    {
        //[ADC_INIT_ARG_MODE] { MP_QSTR_init , MP_ARG_REQUIRED | MP_ARG_BOOL, {.u_bool = false} },
        [UART_INIT_ARG_BAUDRATE] { MP_QSTR_baudrate , MP_ARG_INT, {.u_int = 9600} },
        [UART_INIT_ARG_BITS    ] { MP_QSTR_bits     , MP_ARG_INT, {.u_int = 8}    },
        [UART_INIT_ARG_PARITY  ] { MP_QSTR_parity   , MP_ARG_INT, {.u_int = 0} },
        [UART_INIT_ARG_STOP    ] { MP_QSTR_stop     , MP_ARG_INT, {.u_int = 1}    },

    };

    //printf("machine_uart_obj_init_helper() in.\r\n");

    /* 解析参数 */
    mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
    mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);

    //printf("mp_arg_parse_all() done.\r\n");

    /* pinmux. */
    PORT_SetPinMux(self->rx_pin_obj->io_port, self->rx_pin_obj->gpio_pin, self->rx_pin_af);
    PORT_SetPinMux(self->tx_pin_obj->io_port, self->tx_pin_obj->gpio_pin, self->tx_pin_af);

    /* clock. */
    CLOCK_SetIpSrc(uart_clock_arr[self->uart_id], kCLOCK_IpSrcSysPllAsync);
    CLOCK_EnableClock(uart_clock_arr[self->uart_id]);
    lpuart_config_t lpuart_config =
    {
        .baudRate_Bps = 9600u,
        .parityMode = kLPUART_ParityDisabled,
        .dataBitsCount = kLPUART_EightDataBits,
        .isMsb = false,
        .stopBitCount = kLPUART_OneStopBit,
        .txFifoWatermark = 0U,
        .rxFifoWatermark = 0U,
        .enableRxRTS = false,
        .enableTxCTS = false,
        .txCtsSource = kLPUART_CtsSourcePin,
        .txCtsConfig = kLPUART_CtsSampleAtStart,
        .rxIdleType = kLPUART_IdleTypeStartBit,
        .rxIdleConfig = kLPUART_IdleCharacter1,
        .enableTx = true,
        .enableRx = false
    };
    /* baudrate. */
    if (args[UART_INIT_ARG_BAUDRATE].u_int > 0)
    {
        lpuart_config.baudRate_Bps = args[UART_INIT_ARG_BAUDRATE].u_int;
    }
    else
    {
        mp_raise_ValueError(MP_ERROR_TEXT("unavailable param: baudrate."));
    }
    /* bits. */
    if (args[UART_INIT_ARG_BITS].u_int == 8)
    {
        lpuart_config.dataBitsCount = kLPUART_EightDataBits;
    }
    else
    {
        mp_raise_ValueError(MP_ERROR_TEXT("unavailable param: bits."));
    }
    /* parity. */
    if (args[UART_INIT_ARG_PARITY].u_int == 0) /* none, even, odd. */
    {
        lpuart_config.parityMode = kLPUART_ParityDisabled;
    }
    else
    {
        mp_raise_ValueError(MP_ERROR_TEXT("unavailable param: parity."));
    }
    /* stop bits. */
    if (args[UART_INIT_ARG_STOP].u_int == 1)
    {
        lpuart_config.stopBitCount = kLPUART_OneStopBit;
    }
    else if (args[UART_INIT_ARG_STOP].u_int == 2)
    {
        lpuart_config.stopBitCount = kLPUART_TwoStopBit;
    }
    else
    {
        mp_raise_ValueError(MP_ERROR_TEXT("unavailable param: stop."));
    }
    LPUART_Init(self->uart_port, &lpuart_config, CLOCK_GetIpFreq(uart_clock_arr[self->uart_id]));

    /* keep the conf settings in uart_obj. */
    self->conf->baudrate = args[UART_INIT_ARG_BAUDRATE].u_int;
    //self->conf->bits     = args[UART_INIT_ARG_BITS].u_int;
    //self->conf->parity   = args[UART_INIT_ARG_PARITY].u_int;
    //self->conf->stop     = args[UART_INIT_ARG_STOP].u_int;

    rbuf_init(&machine_uart_rx_rbuf[self->uart_id], machine_uart_rx_buff[self->uart_id], MACHINE_UART_XFER_BUFF_LEN);
    self->conf->rx_rbuf = &machine_uart_rx_rbuf[self->uart_id];


    /* Enable only  RX interrupt. */
    LPUART_EnableInterrupts(self->uart_port, kLPUART_RxDataRegFullInterruptEnable);
    NVIC_EnableIRQ(self->uart_irqn);

    /* Enable UART. */
    LPUART_EnableRx(self->uart_port, true);

    return mp_const_none;
}


bool machine_uart_tx_data(uint32_t uart_id, uint8_t tx_data)
{
    const machine_uart_obj_t * uart_obj = machine_uart_objs[uart_id];

    LPUART_Type * uartx = uart_obj->uart_port;

    LPUART_WriteBlocking(uartx, &tx_data, 1u);

    //while ( 0u == (kLPUART_TxDataRegEmptyFlag & LPUART_GetStatusFlags(uartx)) )
    //{}
    //LPUART_WriteByte(uartx, tx_data);

    return true;
}

uint8_t machine_uart_rx_data(uint32_t uart_id)
{
    const machine_uart_obj_t * uart_obj = machine_uart_objs[uart_id];
    rbuf_t *rx_rbuf = uart_obj->conf->rx_rbuf;

    while ( rbuf_is_empty(rx_rbuf) )
    {
    }

    return rbuf_output(rx_rbuf);
}

uint32_t machine_uart_rx_any(uint32_t uart_id)
{
    const machine_uart_obj_t * uart_obj = machine_uart_objs[uart_id];
    rbuf_t *rx_rbuf = uart_obj->conf->rx_rbuf;
    return rbuf_count(rx_rbuf);
}

/* this function would be called in hardware irq. */
void machine_uart_irq_handler(uint32_t uart_id)
{
    const machine_uart_obj_t * uart_obj = machine_uart_objs[uart_id];
    LPUART_Type * uartx = uart_obj->uart_port;
    rbuf_t * rx_rbuf = uart_obj->conf->rx_rbuf;

    /* rx process. */
    //if (   (0u != (kLPUART_RxDataRegFullInterruptEnable & LPUART_GetEnabledInterrupts(uartx)))
    //    && (0u != (kLPUART_RxDataRegFullFlag & LPUART_GetStatusFlags(uartx))) )
    {
        if ( !rbuf_is_full(rx_rbuf) )
        {
            rbuf_input(rx_rbuf, LPUART_ReadByte(uartx)); /* read data to clear rx interrupt bits. */
        }
    }
}

void LPUART0_RX_IRQHandler(void) { machine_uart_irq_handler(0u); }
void LPUART1_RX_IRQHandler(void) { machine_uart_irq_handler(1u); }
void LPUART2_RX_IRQHandler(void) { machine_uart_irq_handler(2u); }


STATIC void machine_uart_obj_print(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind)
{
    /* o is the machine_pin_obj_t. */
    (void)kind;
    const machine_uart_obj_t *self = MP_OBJ_TO_PTR(o);
    //mp_printf(print, "ADC(%s)", qstr_str(self->adc_channel));
    mp_printf(print, "UART(%d): baudrate=%d on RX(%s), TX(%s)",
        self->uart_id, self->conf->baudrate,
        qstr_str(self->rx_pin_obj->name),
        qstr_str(self->tx_pin_obj->name)
        );
}

/* return an instance of machine_pin_obj_t. */
mp_obj_t machine_uart_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args)
{
    mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);

    const machine_uart_obj_t *uart_obj = uart_find(args[0]);

    if ( (n_args >= 1) || (n_kw >= 0) )
    {
        mp_map_t kw_args;
        mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); /* 将关键字参数从总的参数列表中提取出来，单独封装成kw_args。 */
        machine_uart_obj_init_helper(uart_obj, n_args - 1, args + 1, &kw_args);
    }

    return (mp_obj_t)uart_obj;
}

/* class locals_dict_table. */
STATIC const mp_rom_map_elem_t machine_uart_locals_dict_table[] =
{
    /* Class instance methods. */
    { MP_ROM_QSTR(MP_QSTR_init),     MP_ROM_PTR(&machine_uart_init_obj)             },
    { MP_ROM_QSTR(MP_QSTR_any),      MP_ROM_PTR(&machine_uart_any_obj)              },
    { MP_ROM_QSTR(MP_QSTR_read),     MP_ROM_PTR(&mp_stream_read_obj)                },
    { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
    { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj)            },
    { MP_ROM_QSTR(MP_QSTR_write),    MP_ROM_PTR(&mp_stream_write_obj)               },

    /* class constants. */
    { MP_ROM_QSTR(MP_QSTR_PARITY_NONE), MP_ROM_INT(UART_PARITY_NONE ) },
    { MP_ROM_QSTR(MP_QSTR_PARITY_EVEN), MP_ROM_INT(UART_PARITY_EVEN ) },
    { MP_ROM_QSTR(MP_QSTR_PARITY_ODD ), MP_ROM_INT(UART_PARITY_ODD  ) },
};
STATIC MP_DEFINE_CONST_DICT(machine_uart_locals_dict, machine_uart_locals_dict_table);


STATIC mp_uint_t machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode)
{
    const machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);

    //if (self->uart_id == 0)
    //{
    //    mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("UART(0) can't read"));
    //}

    // make sure we want at least 1 char
    if (size == 0)
    {
        return 0;
    }

    // read the data
    uint8_t *buf = buf_in;
    for (;;)
    {
        if (machine_uart_rx_any(self->uart_id) == 0)
        {
            return buf - (uint8_t *)buf_in;
        }
        *buf++ = machine_uart_rx_data(self->uart_id);
        if (--size == 0 /* || !uart_rx_wait(self->timeout_char * 1000)*/ )
        {
            // return number of bytes read
            return buf - (uint8_t *)buf_in;
        }
    }
}

STATIC mp_uint_t machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode)
{
    machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
    const byte *buf = buf_in;

    /* TODO implement non-blocking
    // wait to be able to write the first character
    if (!uart_tx_wait(self, timeout)) {
        *errcode = EAGAIN;
        return MP_STREAM_ERROR;
    }
    */

    // write the data
    for (size_t i = 0; i < size; ++i)
    {
        machine_uart_tx_data(self->uart_id, *buf++);
    }

    // return number of bytes written
    return size;
}

STATIC mp_uint_t machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode)
{
    machine_uart_obj_t * self = (machine_uart_obj_t *)self_in;
    mp_uint_t ret;

    if (request == MP_STREAM_POLL)
    {
        mp_uint_t flags = arg;
        ret = 0;
        if ( (flags & MP_STREAM_POLL_RD) && machine_uart_rx_any(self->uart_id) )
        {
            ret |= MP_STREAM_POLL_RD;
        }
        //if ((flags & MP_STREAM_POLL_WR) && uart_tx_any_room(self->uart_id))
        if (flags & MP_STREAM_POLL_WR)
        {
            ret |= MP_STREAM_POLL_WR;
        }
    }
    else
    {
        *errcode = MP_EINVAL;
        ret = MP_STREAM_ERROR;
    }
    return ret;
}

STATIC const mp_stream_p_t uart_stream_p =
{
    .read  = machine_uart_read,
    .write = machine_uart_write,
    .ioctl = machine_uart_ioctl,
    .is_text = false,
};

const mp_obj_type_t machine_uart_type =
{
    { &mp_type_type },
    .name     = MP_QSTR_UART,
    .print    = machine_uart_obj_print, /* __repr__(), which would be called by print(<ClassName>). */
    .make_new = machine_uart_obj_make_new, /* create new class instance. */
    .getiter  = mp_identity_getiter,
    .iternext = mp_stream_unbuffered_iter,
    .protocol = &uart_stream_p,
    .locals_dict = (mp_obj_dict_t *)&machine_uart_locals_dict,
};

/*
 * User functions.
 */
/* 格式化uart对象，传入参数无论是已经初始化好的uart对象，还是一个表示uart清单中的索引编号，通过本函数都返回一个期望的uart对象。 */
const machine_uart_obj_t *uart_find(mp_obj_t user_obj)
{
    /* 如果传入参数本身就是一个uart的实例，则直接送出这个UART。 */
    if ( mp_obj_is_type(user_obj, &machine_uart_type) )
    {
        return user_obj;
    }

    /* 如果传入参数是一个uart通道号，则通过索引在UART清单中找到这个通道，然后送出这个通道。 */
    if ( mp_obj_is_small_int(user_obj) )
    {
        uint8_t uart_idx = MP_OBJ_SMALL_INT_VALUE(user_obj);
        if ( uart_idx < machine_uart_num )
        {
            return machine_uart_objs[uart_idx];
        }
    }

    mp_raise_ValueError(MP_ERROR_TEXT("UART doesn't exist"));
}

/* EOF. */

